package vip.xiaonuo.modular.sso;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.ContentType;
import com.alibaba.druid.util.HttpClientUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Striped;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StopWatch;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import vip.xiaonuo.core.context.constant.ConstantContextHolder;
import vip.xiaonuo.core.context.login.LoginContext;
import vip.xiaonuo.core.context.login.LoginContextHolder;
import vip.xiaonuo.core.pojo.response.ErrorResponseData;
import vip.xiaonuo.core.pojo.response.SuccessResponseData;
import vip.xiaonuo.core.util.CryptogramUtil;
import vip.xiaonuo.core.util.HttpServletUtil;
import vip.xiaonuo.sys.core.enums.AdminTypeEnum;
import vip.xiaonuo.sys.modular.auth.service.AuthService;
import vip.xiaonuo.sys.modular.auth.service.impl.LogoutEvent;
import vip.xiaonuo.sys.modular.syscollege.entity.SysCollege;
import vip.xiaonuo.sys.modular.syscollege.service.SysCollegeService;
import vip.xiaonuo.sys.modular.user.entity.SysUser;
import vip.xiaonuo.sys.modular.user.param.SysUserParam;
import vip.xiaonuo.sys.modular.user.service.SysUserService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.regex.Pattern;

import static org.apache.commons.lang.CharEncoding.UTF_8;

@Slf4j

public class NjuSsoInterceptor extends HandlerInterceptorAdapter {
    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    private static final List<String> STUDENT_PREFIXES = Lists.newArrayList("dg", "dz", "df", "mg", "mf", "mp");
    private static final Pattern NO_PATTERN = Pattern.compile("\\w+\\d+");
    private Set<String> excludes = Sets.newHashSet();

    @SuppressWarnings("UnstableApiUsage")
    private Striped<Lock> locks = Striped.lazyWeakLock(20);

    private final SysUserService userService;
    private final AuthService authService;
    private final SysCollegeService sysCollegeService;
    @Value("${nju.teacher-account}")
    private String[] TEACHER_ACCOUNT;   //可进入的教师名单

    public NjuSsoInterceptor(SysUserService userService, AuthService authService, SysCollegeService sysCollegeService) {
        this.userService = userService;
        this.authService = authService;
        this.sysCollegeService = sysCollegeService;
    }

    @Override
    @SuppressWarnings("UnstableApiUsage")
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String account = getUserAttr(request, "CAS-USER");
//        String account ="800000";
        log.info("可进入的教师学号："+Arrays.toString(TEACHER_ACCOUNT));
        if (!isIgnoreOrLoggedIn(request) && StringUtils.isNotBlank(account)) {
            Lock lock = locks.get(account);
            try {
                lock.lock();
                //初始化CAS用户信息
                Assert.isTrue(NO_PATTERN.matcher(account).matches(), "CAS用户信息有误，请联系本系统的运维人员进行确认");
//                String name = getUserAttr(request, "CAS-USER-cn");
//                String phone = getUserAttr(request, "CAS-USER-telephonenumber");
//                String email = getUserAttr(request, "CAS-USER-securityemail");
//                //院系,此字段可能没有值
//                String coll = getUserAttr(request, "CAS-USER-eduPersonOrgDN");
//
//                //TODO 院系初始化逻辑
//                Long collId = getOrCreateCollege(coll);
                //账号是否是学生
                SysUser sysUser = userService.getUserByCount(account);
                if (sysUser == null) {

                    response.setCharacterEncoding(UTF_8);
                    response.setContentType(ContentType.JSON.toString());
                    ErrorResponseData errorResponseData=new ErrorResponseData(400,"您不在系统中，请联系管理员");
                    String json = JSON.toJSONString(errorResponseData);
                    response.getWriter().write(json);
                    return false;

                    //-------------原cas代码-----------------------------
//                    SysUserParam sysUserParam = new SysUserParam();
//                    sysUserParam.setAccount(account);
//                    sysUserParam.setUserNum(account);
//                    sysUserParam.setName(name);
//                    sysUserParam.setPhone("13800000000");//将手机号设为固定值13800000000
//                    sysUserParam.setEmail(email);
//                    sysUserParam.setCollId(collId);
//                    sysUserParam.setUserType(isStudent(account) ? AdminTypeEnum.STUDENT.getLabel() : AdminTypeEnum.TEACHER.getLabel());
//                    //若是老师，则判断该老师是否可以进入该系统
//                    if(sysUserParam.getUserType()!=null&&sysUserParam.getUserType().equals("tch")){
//                        List<String> list= Arrays.asList(TEACHER_ACCOUNT);
//                        boolean flag = list.contains(account);
//                        if(!flag){
//                            sysUserParam.setStatus(1);
//                        }
//                    }
//                    userService.add(sysUserParam);
//                    sysUser = userService.getUserByCount(name);
                } else {
                    //update if necessary
//                    sysUser.setPhone(ConstantContextHolder.getCryptogramConfigs().getFieldEncDec() ? CryptogramUtil.doEncrypt(phone) : phone);
//                    sysUser.setPhone("13800000000");//将手机号设为固定值13800000000
//                    sysUser.setEmail(email);
//                    userService.updateById(sysUser);
                }
                //write token to frontend
                String token = authService.doLogin(sysUser);
                log.info("token is : {}", token);
                writeToken(response, token);
                stopWatch.stop();
                System.out.printf(">>>>>>>>>>>>>times1="+ stopWatch.getTotalTimeMillis());
                return false;
            } finally {
                lock.unlock();
            }
        }
        stopWatch.stop();
        System.out.printf(">>>>>>>>>>>>>times2="+ stopWatch.getTotalTimeMillis());
        return super.preHandle(request, response, handler);
    }

    private Long getOrCreateCollege(String coll) {
        //原逻辑：没有部门则为空，先逻辑：没有部门的都归属到化学化工学院
        if (StringUtils.isNotBlank(coll)) {
            LambdaQueryWrapper<SysCollege> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SysCollege::getName, coll);
            SysCollege c = sysCollegeService.getOne(queryWrapper);
            if (c == null) {
                //原逻辑：部门不存在则创建，现在不存在整体归属到化学化工学院
//                c = new SysCollege();
//                c.setName(coll);
//                c.setStatus(0);
//                String code = RandomUtil.randomString(10);
//                c.setCode(code);
//                sysCollegeService.save(c);
                LambdaQueryWrapper<SysCollege> queryWrapper1 = new LambdaQueryWrapper<>();
                queryWrapper1.eq(SysCollege::getName, "化学化工学院");
                SysCollege cc = sysCollegeService.getOne(queryWrapper1);
                return cc.getId();
            }
            return c.getId();
        }else {
            LambdaQueryWrapper<SysCollege> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SysCollege::getName, "化学化工学院");
            SysCollege c = sysCollegeService.getOne(queryWrapper);
            return c.getId();
        }

    }

    private boolean hasLogin() {
        LoginContext loginContext = LoginContextHolder.me();
        return loginContext.hasLogin();
    }

    @SneakyThrows
    private void writeToken(HttpServletResponse response, String token) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        response.setCharacterEncoding(UTF_8);
        response.setContentType(ContentType.JSON.toString());
        SuccessResponseData responseData = new SuccessResponseData(888, "CAS login success", token);
        String json = JSON.toJSONString(responseData);
        response.getWriter().write(json);
        response.flushBuffer();
        stopWatch.stop();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>newresponseTimes"+stopWatch.getTotalTimeMillis());
    }

    @SneakyThrows
    private String getUserAttr(HttpServletRequest request, String attrName) {
        String attrVal = request.getHeader(attrName);
        if (StringUtils.isBlank(attrVal)) {
            attrVal = StringUtils.EMPTY;
        }
        attrVal = URLDecoder.decode(attrVal, UTF_8);
        log.info("{} : {}", attrName, attrVal);
        return attrVal;
    }

    private boolean isStudent(String no) {
        String prefix = StringUtils.lowerCase(no.substring(0, 2));
        int index = STUDENT_PREFIXES.indexOf(prefix);
        return index >= 0 || (NumberUtils.toLong(no) > 0 && no.length() == 9);
    }

    private boolean isIgnoreOrLoggedIn(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        for (String pattern : excludes) {
            if (PATH_MATCHER.match(pattern, requestURI)) {
                return true;
            }
        }
        return hasLogin();
    }

    @EventListener(LogoutEvent.class)
    @SneakyThrows
    public void onLogout(LogoutEvent event) {
        HttpServletRequest request = event.getRequest();
        String logoutUrl = request.getHeader("CAS-LOGOUT-URL");
        if (StringUtils.isNotBlank(logoutUrl)) {
            log.info("CAS Logout URL: {}", logoutUrl);
            HttpServletUtil.getResponse().sendRedirect(logoutUrl);
        }
    }
}
